#include "octree.h"

#include <QDebug>

// OctreeNode Constructor
// origin       - central point of the bounding box
// dimension    - size in terms of space
// depth        - the maximum number of subdivisions that can occur
OctreeNode::OctreeNode( const glm::vec3 &origin, float dimension, unsigned int depth )
    :m_is_leaf(false),
      m_depth(depth),                                    // This node's depth
      m_node_boundbox(origin, dimension),                // This node's boundbox
      m_state(Outside)
{
}

//// Just calls the scene's primitives collision methods against this node and updates this nodes state
//void OctreeNode::verifyCollision()
//{
//    QVector<QSharedPointer<Primitive> >::iterator scene_iter;
//    CollisionState temp = Outside;
//    m_state = Outside;  // In case we have an empty scene - the node is outside by default
//    for ( scene_iter = m_current_scene.begin(); scene_iter != m_current_scene.end(); scene_iter++ ) {
//        temp = m_node_boundbox.doCollide(*(*scene_iter));
//        if ( temp < m_state ) { // the smallest is inside(0) followed by partially and outside
//            m_state = temp;
//        }
//    }
//}

//bool OctreeNode::isLeaf() const
//{
//    return m_is_leaf;
//}

//const Cube & OctreeNode::getBoundary() const
//{
//    return m_node_boundbox;
//}

//CollisionState OctreeNode::getCollisionState() const
//{
//    return m_state;
//}

//void OctreeNode::subdivide()                       // Not a leaf node? subdivide
//{
//    // updates collision information
//    verifyCollision();
//    if (m_depth == 0) {   // end of the road
//        //qDebug() << "DEPTH REACHED 0";
//        if ( m_state == Outside ) {
//            //qDebug() << "Node with center: " << m_node_boundbox.m_origin.x << ", " << m_node_boundbox.m_origin.y << ", "
//                     << m_node_boundbox.m_origin.z << " and side: " << m_node_boundbox.m_side << " is OUTSIDE and won't subdivide.";
//        } else if (m_state == Partially ){
//            //qDebug() << "Node with center: " << m_node_boundbox.m_origin.x << ", " << m_node_boundbox.m_origin.y << ", "
//                     << m_node_boundbox.m_origin.z << " and side: " << m_node_boundbox.m_side << " is PARTIALLY and won't subdivide.";
//        } else {
//            //qDebug() << "Node with center: " << m_node_boundbox.m_origin.x << ", " << m_node_boundbox.m_origin.y << ", "
//                     << m_node_boundbox.m_origin.z << " and side: " << m_node_boundbox.m_side << " is INSIDE and won't subdivide.";
//        }
//        // Mark this as a leaf anyway
//        m_is_leaf = true;
//    } else {
//        if ( m_state == Partially ) {
////            //qDebug() << "SUBDIVIDING Node with center: " << m_node_boundbox.m_origin.x << ", " << m_node_boundbox.m_origin.y << ", "
////                     << m_node_boundbox.m_origin.z << " and side: " << m_node_boundbox.m_side;
//            // Instantiate the eight new son nodes(trees)
//            Point3f lowest = m_node_boundbox.m_origin;      // this node's origin
//            Point3f temp;
//            float fraction = m_node_boundbox.m_side/2.0f;
//            // The lowest son node's center
//            lowest.x -= fraction/2.0f;
//            lowest.y -= fraction/2.0f;
//            lowest.z -= fraction/2.0f;
//            temp = lowest;
//            for ( int i = 0; i < 2; i++) {
//                temp.y = lowest.y;
//                for ( int j = 0; j < 2; j++) {
//                    temp.z = lowest.z;
//                    for ( int k = 0; k < 2; k++) {
//                        // A new son octree
//                        m_son.push_back( QSharedPointer<OctreeNode>(new OctreeNode(temp, fraction, m_depth-1, m_current_scene)));
//                        // Subdivide the new son - maybe we could postpone this and centralize the sons subdivision? nah
//                        m_son.last()->subdivide();
//                        temp.z += fraction;
//                    }
//                    temp.y += fraction;
//                }
//                temp.x += fraction;
//            }
//        } else {
//            if ( m_state == Inside ) {
//                //qDebug() << "Node with center: " << m_node_boundbox.m_origin.x << ", " << m_node_boundbox.m_origin.y << ", "
//                         << m_node_boundbox.m_origin.z << " and side: " << m_node_boundbox.m_side << " is INSIDE and won't subdivide.";
//            } else {
//                //qDebug() << "Node with center: " << m_node_boundbox.m_origin.x << ", " << m_node_boundbox.m_origin.y << ", "
//                         << m_node_boundbox.m_origin.z << " and side: " << m_node_boundbox.m_side << " is OUTSIDE and won't subdivide.";
//            }
////            //qDebug() << "Depth is not Zero but node is outside. Node with center: " << m_node_boundbox.m_origin.x << ", " << m_node_boundbox.m_origin.y << ", "
////                     << m_node_boundbox.m_origin.z << " and side: " << m_node_boundbox.m_side;
//            m_is_leaf = true;
//        }
//    }
//}

//Constructs octree with root node as the given values
Octree::Octree(const glm::vec3 &origin, float dimension, int max_depth){

    m_root = new OctreeNode(origin,dimension,0);
    m_curr_node = m_root;
    m_nodes.push_back(m_root);
    m_max_depth = max_depth;
    m_visible = true;
    m_solid = true;
    m_wireframe = true;
    m_show_in_nodes = true;
    m_show_out_nodes = true;
    m_show_partial_nodes = true;
}

Octree::~Octree(){    
    QList<OctreeNode*>::iterator iter;
    iter = m_nodes.begin();
    //Reads the list and delete all elements
    while(!m_nodes.empty()){
        delete (*iter);
        m_nodes.removeFirst();
        iter = m_nodes.begin();
    }
}

void Octree::insert_node(OctreeNode *node){
    m_nodes.push_back(node);
}

void Octree::subdivide_node(OctreeNode *node){
    OctreeNode *child_node = 0;
    verify_collision(node);
    if(!node->m_is_leaf){
        // updates collision information of the given node
        if (node->m_depth == m_max_depth) {   // end of the road
            //qDebug() << "MAX DEPTH REACHED";
            if ( node->m_state == Outside ) {
                //qDebug() << "Node with center: " << node->m_node_boundbox.m_origin.x << ", " << node->m_node_boundbox.m_origin.y << ", "
                         //<< node->m_node_boundbox.m_origin.z << " and side: " << node->m_node_boundbox.m_side << " is OUTSIDE and won't subdivide.";
            } else if (node->m_state == Partially ){
                //qDebug() << "Node with center: " << node->m_node_boundbox.m_origin.x << ", " << node->m_node_boundbox.m_origin.y << ", "
                         //<< node->m_node_boundbox.m_origin.z << " and side: " << node->m_node_boundbox.m_side << " is PARTIALLY and won't subdivide.";
            } else {
                //qDebug() << "Node with center: " << node->m_node_boundbox.m_origin.x << ", " << node->m_node_boundbox.m_origin.y << ", "
                         //<< node->m_node_boundbox.m_origin.z << " and side: " << node->m_node_boundbox.m_side << " is INSIDE and won't subdivide.";
            }
            //qDebug() << "Total number of nodes is:" << m_nodes.size();
            // Mark this as a leaf anyway
            node->m_is_leaf = true;
        } else {
            if ( node->m_state == Partially ) {
<<<<<<< HEAD
               //qDebug() << "SUBDIVIDING Node with center: " << node->m_node_boundbox.m_origin.x << ", " << node->m_node_boundbox.m_origin.y << ", "
                        //<< node->m_node_boundbox.m_origin.z << " and side: " << node->m_node_boundbox.m_side;
=======
                qDebug() << "SUBDIVIDING Node with center: " << node->m_node_boundbox.m_origin.x << ", " << node->m_node_boundbox.m_origin.y << ", "
                         << node->m_node_boundbox.m_origin.z << " and side: " << node->m_node_boundbox.m_side;
>>>>>>> dccd3380a79c37420c4d14271314caf7b5f39b58
                // Instantiate the eight new son nodes(trees)
                glm::vec3 lowest = node->m_node_boundbox.m_origin;      // this node's origin
                glm::vec3 temp;
                float fraction = node->m_node_boundbox.m_side/2.0f;
                // The lowest son node's center
                lowest.x -= fraction/2.0f;
                lowest.y -= fraction/2.0f;
                lowest.z -= fraction/2.0f;
                temp = lowest;
                for ( int i = 0; i < 2; i++) {
                    temp.y = lowest.y;
                    for ( int j = 0; j < 2; j++) {
                        temp.z = lowest.z;
                        for ( int k = 0; k < 2; k++) {
                            // A new son octree
                            child_node = new OctreeNode(temp, fraction, node->m_depth+1 );
                            node->m_children.push_back( QSharedPointer<OctreeNode>(child_node) );
                            this->m_nodes.push_back(child_node);
                            // Subdivide the new son - maybe we could postpone this and centralize the sons subdivision? nah
                            //this->subdivide_node(node->m_children.last());
                            temp.z += fraction;
                        }
                        temp.y += fraction;
                    }
                    temp.x += fraction;
                }
            } else {
                if ( node->m_state == Inside ) {
                    //qDebug() << "Node with center: " << node->m_node_boundbox.m_origin.x << ", " << node->m_node_boundbox.m_origin.y << ", "
                             //<< node->m_node_boundbox.m_origin.z << " and side: " << node->m_node_boundbox.m_side << " is INSIDE and won't subdivide.";
                } else {
                    //qDebug() << "Node with center: " << node->m_node_boundbox.m_origin.x << ", " << node->m_node_boundbox.m_origin.y << ", "
                             //<< node->m_node_boundbox.m_origin.z << " and side: " << node->m_node_boundbox.m_side << " is OUTSIDE and won't subdivide.";
                }
<<<<<<< HEAD
    //            //qDebug() << "Depth is not Zero but node is outside. Node with center: " << m_node_boundbox.m_origin.x << ", " << m_node_boundbox.m_origin.y << ", "
    //                     << m_node_boundbox.m_origin.z << " and side: " << m_node_boundbox.m_side;
=======
                //            qDebug() << "Depth is not Zero but node is outside. Node with center: " << m_node_boundbox.m_origin.x << ", " << m_node_boundbox.m_origin.y << ", "
                //                     << m_node_boundbox.m_origin.z << " and side: " << m_node_boundbox.m_side;
>>>>>>> dccd3380a79c37420c4d14271314caf7b5f39b58
                node->m_is_leaf = true;
            }
        }
    }
}

void Octree::verify_collision(OctreeNode *node){
    CollisionState temp_state = Outside;
    node->m_state = Outside; // In case we have an empty scene - the node is outside by default
    Object* curr_obj = 0;
    int curr_obj_pos;
    for(curr_obj_pos = 0; curr_obj_pos < m_current_scene->get_num_objs();curr_obj_pos++){
        curr_obj = m_current_scene->get_object(curr_obj_pos);
        if(curr_obj != NULL){
            temp_state = node->m_node_boundbox.doCollide(*(curr_obj->get_primitive()));

            if(temp_state < node->m_state){
                node->m_state = temp_state;
            }
        }
    }
}

void Octree::subdivide(Scene *scene, int max_depth){
    OctreeNode* curr_node = 0;
    m_current_scene = scene;
    m_max_depth = max_depth;
    int num_nodes = m_nodes.size();
    for(int i = 0; i < num_nodes; i++){
        curr_node = m_nodes[i];
        if(curr_node != 0){
            subdivide_node(curr_node);
        }else{
            break;
        }
        num_nodes = m_nodes.size();
    }
}

void Octree::set_visible(bool visible){
    m_visible = visible;
}

bool Octree::is_visible(){
    return m_visible;
}

bool Octree::wireframe(){
    return m_wireframe;
}

void Octree::set_nodes_visibility(CollisionState state, bool visibility){
    switch(state){
    case Inside:
        m_show_in_nodes = visibility;
        break;
    case Outside:
        m_show_out_nodes = visibility;
        break;
    case Partially:
        m_show_partial_nodes = visibility;
        break;
    }
}

bool Octree::get_nodes_visibility(CollisionState state){
    switch(state){
    case Inside:
        return m_show_in_nodes;
    case Outside:
        return m_show_out_nodes;
    case Partially:
        return m_show_partial_nodes;
    }
}


bool Octree::solid(){
    return m_solid;
}

void Octree::set_wireframe(bool wireframe){
    m_wireframe = wireframe;
}

void Octree::set_solid(bool solid){
    m_solid = solid;
}
